<template>
  <div
    v-loading="loading"
    class="rc-cont">
    <flexbox
      class="rc-head"
      direction="row-reverse">

      <el-popover
        v-model="filterShow"
        popper-class="no-padding-popover"
        placement="bottom"
        width="300"
        trigger="click">

        <map-filter
          v-if="filterShow"
          type="relation"
          :filter-obj="filterData"
          @close="filterShow = false"
          @save="filterSave"
        />

        <el-button
          slot="reference"
          v-model="filterShow"
          :type="filterShow ? 'selected' : 'subtle'"
          icon="wk wk-screening"
          @click="filterShow = true">筛选</el-button>
      </el-popover>
    </flexbox>

    <div ref="d3Box" class="d3-tree-box">
      <div ref="bar" class="seeTree-page">
        <div v-if="showTree" :id="d3Id" style="width: 100%; height: 100%;" />
      </div>
      <div class="toolbar">
        <ul>
          <li @click="onFullScreen">
            <i class="el-icon-full-screen" />
            <span>全屏</span>
          </li>
          <li @click="onMagnifySVG">
            <i class="el-icon-zoom-in" />
            <span>放大</span>
          </li>
          <li @click="onShrinkSVG">
            <i class="el-icon-zoom-out" />
            <span>缩小</span>
          </li>
          <li @click="svgDownloadAll">
            <i class="el-icon-download" />
            <span>保存</span>
          </li>
        </ul>
      </div>
    </div>

    <c-r-m-full-screen-detail
      :id="detailId"
      :visible.sync="showFullDetail"
      :crm-type="crmType"
      :no-listener-class="['rc-cont']"
      @handle="detailHandle" />
  </div>
</template>

<script type="text/javascript">
import { crmCustomerQueryCustomerAtlastListAPI } from '@/api/crm/customer'

import MapFilter from './components/MapFilter'

// 引入d3
import * as d3 from 'd3'

let leng
let circlewidth1
import { guid } from '@/utils/index'

export default {
  name: 'EnterpriseMap', // 客户图谱
  components: {
    MapFilter,
    CRMFullScreenDetail: () => import('@/components/CRMFullScreenDetail')
  },
  mixins: [],
  inject: ['rootTabs'],
  props: {
    // 模块ID
    id: [String, Number],
    detail: {
      type: Object,
      default: () => {
        return {}
      }
    }
  },
  data() {
    return {
      svg: null,
      d3Id: this.randomString(),
      container: null, // 容器svg>g
      duration: 500, // 动画持续时间
      zoom: null,
      scaleRange: [0.2, 2], // container缩放范围
      direction: ['r', 'l'], // 分为左右2个方向
      centralPoint: [0, 0], // 画布中心点坐标x,y
      root: { r: {}, l: {}}, // 左右2块数据源
      rootNodeLength: 0, // 根节点名称长度
      rootName: ['', ''], // 根节点名称
      textSpace: 15, // 多行文字间距
      themeColor: '#0052CC', // 主色
      nodeSize: [50, 100], // 节点间距(高/水平)
      fontSize: 14, // 字体大小，也是单字所占宽高
      rectMinWidth: 50, // 节点方框默认最小，
      textPadding: 5, // 文字与方框间距,注：固定值5
      circleR: 5, // 圆圈半径
      forUpward: null, // 向右为true
      treeData: {
        rootName: '',
        r: {
          name: 'origin',
          children: []
        },
        l: {
          name: 'origin',
          children: []
        }
      },

      isScreenState: false, // 是否全屏展示
      isShowExpandAll: true, // 是否展示所有扩展数据

      clickNodeElement: '', // 当前点击node
      clickNodeData: '', // 当前点击node
      loading: false,
      detailId: '',
      crmType: '',
      showFullDetail: false,

      showTree: true,
      filterShow: false, // 筛选显示
      filterData: null // 筛选条件
    }
  },
  computed: {
    treeMap() {
      return d3
        .tree()
        .nodeSize(this.nodeSize)
        .separation((a, b) => {
          const result =
            a.parent === b.parent && !a.children && !b.children ? 1 : 2
          return result
        })
    }
  },
  watch: {
    'rootTabs.currentName'(val) {
      if (val === 'enterprise') {
        this.getData()
      }
    }
  },
  created() {
    // 初始化屏幕状态
    this.isScreenState = !!(document.webkitIsFullScreen || document.mozFullScreen || document.msFullscreenElement || document.fullscreenElement)
    // 屏幕状态变化监听
    window.onresize = () => {
      this.isScreenState = !!(document.webkitIsFullScreen || document.mozFullScreen || document.msFullscreenElement || document.fullscreenElement)
    }
  },
  mounted() {
  },
  methods: {
    detailHandle() {

    },

    /**
     * 筛选
     */
    filterSave(data) {
      this.treeData = {
        rootName: '',
        r: {
          name: 'origin',
          children: []
        },
        l: {
          name: 'origin',
          children: []
        }
      }
      this.showTree = false
      this.filterData = data
      this.filterShow = false
      this.getData()
      // this.filterDataFun()
    },

    filterDataFun() {
      console.log(this.treeData, 'treeData')
      // this.treeData.l.children
    },

    getData() {
      this.treeData.rootName = this.getShowNodeName(this.detail.customerName)

      const filterData = this.getShowModuleFilterData()

      const params = {
        customerId: this.id,
        ...filterData,
        relationsIds: this.filterData ? this.filterData.relationTypeArr : []
      }
      crmCustomerQueryCustomerAtlastListAPI(params)
        .then(res => {
          // 展示在左侧
          const l = {
            name: 'origin',
            children: []
          }

          const resData = res.data || {}
          this.setModuleData(filterData, l.children, resData)

          console.log('l', l)
          // 展示在右侧
          const r = {
            name: 'origin',
            children: []
          }
          if (res.data.relationsList.length) {
            this.setRightData(r.children, resData, params)
          }
          this.treeData.l = l
          this.treeData.r = r
          this.showTree = true

          this.rootName = [this.treeData.rootName, '']
          console.log(this.treeData, 'data')

          this.$nextTick(res => {
            this.treeInit(this.treeData)
          })
        })
        .catch(() => {})
    },

    /**
     * @description: 设置模块相关的数据
     * @return {*}
     */
    setModuleData(filterData, originList, resData) {
      if (filterData.isBusiness && (resData.businessList?.length > 0)) {
        originList.push({
          name: '商机',
          nodeType: 0,
          dataType: 'business',
          children: resData.businessList.map(item => {
            return {
              name: this.getShowNodeName(item.businessName),
              id: item.businessId,
              crmType: 'business',
              isEndNode: true
            }
          })
        })
      }
      if (filterData.isContacts && (resData.contactsList?.length > 0)) {
        originList.push({
          name: '联系人',
          nodeType: 0,
          dataType: 'contacts',
          children: resData.contactsList.map(item => {
            return {
              name: this.getShowNodeName(item.name),
              id: item.contactsId,
              crmType: 'contacts',
              isEndNode: true
            }
          })
        })
      }
      if (filterData.isContract && (resData.contractList?.length > 0)) {
        originList.push({
          name: '合同',
          nodeType: 0,
          dataType: 'contract',
          children: resData.contractList.map(item => {
            return {
              name: this.getShowNodeName(item.name),
              id: item.contractId,
              crmType: 'contract',
              isEndNode: true
            }
          })
        })
      }

      if (filterData.isReceivablesPlan && (resData.receivablesPlanList?.length > 0)) {
        originList.push({
          name: '回款计划',
          nodeType: 0,
          dataType: 'receivablesPlan',
          children: resData.receivablesPlanList.map(item => {
            return {
              name: this.getShowNodeName(item.num),
              id: item.receivablesPlanId,
              crmType: 'receivablesPlan',
              isEndNode: true
            }
          })
        })
      }

      if (filterData.isReceivables && (resData.receivablesList?.length > 0)) {
        originList.push({
          name: '回款',
          nodeType: 0,
          dataType: 'receivables',
          children: resData.receivablesList.map(item => {
            return {
              name: this.getShowNodeName(item.number),
              id: item.receivablesId,
              crmType: 'receivables',
              isEndNode: true
            }
          })
        })
      }

      if (filterData.isVisit && (resData.visitList?.length > 0)) {
        originList.push({
          name: '回访',
          nodeType: 0,
          children: resData.visitList.map(item => {
            return {
              name: this.getShowNodeName(item.visitNumber),
              id: item.visitId,
              crmType: 'visit',
              isEndNode: true
            }
          })
        })
      }
      if (filterData.isFollower && (resData.followerList?.length > 0)) {
        originList.push({
          name: '跟进人',
          nodeType: 0,
          dataType: 'follower',
          children: resData.followerList.map(item => {
            return {
              name: this.getShowNodeName(item.realname),
              id: item.userId,
              crmType: 'follower',
              isEndNode: true
            }
          })
        })
      }
    },

    /**
     * @description: 设置上下级数据
     * @param {*} originList
     * @param {*} resData
     * * @param {*} params 请求参数需要用到id参数
     * @return {*}
     */
    setRightData(originList, resData, params) {
      const relationsObj = {}
      resData.relationsList.forEach(item => {
        if (params.relationsIds.length === 0 || params.relationsIds.includes(item.relationsId)) {
          const list = relationsObj[item.relationsId] || []
          list.push(item)
          relationsObj[item.relationsId] = list
        }
      })

      for (const key in relationsObj) {
        const item = relationsObj[key]
        const obj = {
          name: item[0].relationsName,
          nodeType: 0,
          children: item.map(subItem => {
            console.log('subItem.sourceCustomerId---', subItem.sourceCustomerId, this.id)
            return {
              name: subItem.sourceCustomerId === params.customerId ? this.getShowNodeName(subItem.targetCustomerName) : this.getShowNodeName(subItem.sourceCustomerName),
              id: subItem.sourceCustomerId === params.customerId ? subItem.targetCustomerId : subItem.sourceCustomerId,
              crmType: 'customer',
              isLoad: false, // 是否已加载
              children: []
            }
          })
        }
        originList.push(obj)
      }
    },

    /**
     * @description: 获取有效展示名称
     * @param {*} name
     * @return {*}
     */
    getShowNodeName(name) {
      if (name && name.length > 10) {
        return name.slice(0, 10) + '...'
      }
      return name
    },

    /**
     * @description: 初始化
     * @param {*} data
     * @return {*}
     */
    treeInit(data) {
      const margin = { top: 0, right: 0, bottom: 0, left: 0 }
      // 获取屏幕宽高
      const treeWidth = this.$refs.d3Box.clientWidth - margin.left - margin.right // tree容器宽
      const treeHeight = this.$refs.d3Box.clientHeight - margin.top - margin.bottom // tree容器高
      const centralY = treeWidth / 2 + margin.left
      const centralX = treeHeight / 2 + margin.top
      this.centralPoint = [centralX, centralY] // 中心点坐标

      // 根节点字符所占宽度
      this.rootNodeLength = this.rootName[0].length * this.fontSize + 50

      // svg标签 先清除之前创建的
      if (this.svg) {
        this.svg.remove()
      }
      this.svg = d3
        .select(`#${this.d3Id}`)
        .append('svg')
        .attr('class', `d3-tree-svg ${this.d3Id}-tree-svg`)
        .attr('xmlns', 'http://www.w3.org/2000/svg')
        .attr('width', treeWidth)
        .attr('height', treeHeight)
        .attr('font-size', this.fontSize)
        .attr('fill', '#555')

      // g标签
      const margin1 = { top: 50, right: 20, bottom: -20, left: 0 }
      this.container = this.svg
        .append('g')
        .attr('class', `${this.d3Id}-d3container`)
        .attr(
          'transform',
          'translate(' + margin1.left + ',' + margin1.top + ')scale(0.8)'
        )

      // 画出根节点
      this.drawRoot()
      // 指定缩放范围
      this.zoom = d3
        .zoom()
        .scaleExtent(this.scaleRange)
        .on('zoom', this.zoomFn)
      // 动画持续时间
      this.container
        .transition()
        .duration(this.duration)
        .call(this.zoom.transform, d3.zoomIdentity)
      this.svg.call(this.zoom)
      // 数据处理
      // console.log(data, "数据处理");
      this.dealData(data)

      this.$nextTick(() => {
        this.zoom.scaleBy(this.svg, 0.7) // 执行该方法后 会触发zoom事件
        d3.zoomTransform(this.svg.node())
      })
    },

    /**
     * @description: 控制内容容器的缩放
     * @return {*}
     */
    zoomFn() {
      var weizhi = document.querySelector(`.${this.d3Id}-d3container`)
      weizhi.style.transform = '' // 偏移位置
      return this.container
        .attr('transform', d3.zoomTransform(this.svg.node()))
    },

    /**
     * @description: 数据重组
     * @param {*} d
     * @param {*} direction
     * @param {*} source
     * @param {*} appendData
     * @return {*}
     */
    DataReor(d, direction, source, appendData) {
      var setDepth = function(node, num, appendLeaf) {
        // 重新设置depth
        node.depth = num
        if (node.children && node.children.length) {
          // 遍历children
          node.children.forEach(function(item) {
            setDepth(item, num + 1, appendLeaf)
          })
        } else {
          appendLeaf.push(node)
        }
      }

      var setHeight = function(arr, num) {
        // 重新设置height
        var parent = []
        arr.forEach(function(node) {
          node.height = Math.max(num, node.height)
          if (node.parent && parent.indexOf(node.parent) == -1) {
            parent.push(node.parent)
          }
        })

        if (parent.length) {
          setHeight(parent, num + 1)
        }
      }

      var appendLeaf = [] // 增加的叶子节点

      if (appendData.children.length) {
        d.children = []
        appendData.children.forEach(function(item, index) {
          var newNode = d3.hierarchy(item)
          newNode.parent = d
          newNode.height = -1
          setDepth(newNode, d.depth + 1, appendLeaf)
          d.children.push(newNode)
        })
      }

      if (appendLeaf.length) {
        setHeight(appendLeaf, 0)
      }

      source.descendants().forEach((d) => {
        // d._children = d.children
        d.id = direction + guid()
      })
      this.update(d, direction)
    },

    /**
     * @description: 数据处理
     * @param {*} data
     * @return {*}
     */
    dealData(data) {
      this.direction.forEach((item) => {
        this.root[item] = d3.hierarchy(data[item])
        this.root[item].x0 = this.centralPoint[0] // 根节点x坐标
        this.root[item].y0 = this.centralPoint[1] // 根节点Y坐标
        this.$nextTick(res => {
          this.porData(this.root[item], item)
        })
      })
    },

    /**
     * @description: 遍历
     * @param {*} obj
     * @param {*} item
     * @return {*}
     */
    porData(obj, item) {
      // descendants 返回后代节点数组
      obj.descendants().forEach((d) => {
        // d._children = d.children
        d.id = item + guid()
      })
      this.update(obj, item)
    },

    /**
     * @description: 画根节点
     * @return {*}
     */
    drawRoot() {
      const title = this.container
        .append('g')
        .attr('id', `${this.d3Id}-rootTitle`)
        .attr(
          'transform',
          `translate(${this.centralPoint[1]},${this.centralPoint[0]})`
        )
      title
        .append('svg:rect')
        .attr('class', `${this.d3Id}-rootTitle`)
        .attr('y', 0)
        .attr('x', -this.rootNodeLength / 2)
        .attr('width', this.rootNodeLength)
        .attr('height', 0)
        .attr('rx', 5) // 圆角
        .style('fill', this.themeColor)
      this.rootName.forEach((name, index) => {
        title
          .append('text')
          .attr('fill', 'white')
          .attr('y', function() {
            return 5
          })
          .attr('text-anchor', 'middle')
          .style('font-size', function() {
            if (index == 1) {
              return '10px'
            } else {
              return '15px'
            }
          })
          .text(name)

        const lineHeight = (index + 2) * this.textSpace

        d3.select(`#${this.d3Id}-rootTitle rect`)
          .attr('height', lineHeight)
          .attr('y', -lineHeight / 2)
      })
    },

    /**
     * @description: 更新y值
     * @param {*} newArr 包含文本宽度的值
     * @param {*} dirRight 右为1 左为-1
     * @param {*} data 二维数组存储同深度下的数据
     * @return {*}
     */
    seat(newArr, dirRight, data) {
      for (var j = 0; j < newArr.length; j++) {
        if (j != 0) {
          for (var i = 0; i < data[j].length; i++) {
            data[j][i].y = data[j - 1][0].y + newArr[j - 1] - 40
          }
        }
      }
    },

    /**
     * @description: 检测最大长度
     * @param {*} data 二维数组存储同深度下的数据
     * @param {*} dirRight 右为1 左为-1
     * @return {*}
     */
    testLength(data, dirRight) {
      var level = []

      for (var i = 0; i < data.length; i++) {
        var newArr = []

        for (var j = 0; j < data[i].length; j++) {
          const item = data[i][j]
          this.svg
            .append('text')
            .style('font-size', this.fontSize)
            .text(function(d) {
              if (item.data.name) {
                const len = item.data.name?.length
                if (len > 20) {
                  return item.data.name?.substring(0, 20) + '...'
                } else {
                  return item.data.name
                }
              }
            })
            .attr('class', 'd3test')
            .attr('width', function(d) {
              // data.width1 = d3.select(this.getComputedTextLength())._groups[0]
              item.nameWidth = Number(d3.select(this.getComputedTextLength())._groups[0])
              newArr.push(item.nameWidth + 100)
            })
          document.querySelector('.d3test').remove()
        }

        level.push(Math.max.apply(null, newArr))
      }

      this.seat(level, dirRight, data)
    },

    /**
     * @description: 开始绘图
     * @param {*} source 数组数据源
     * @param {*} direction 方向
     * @return {*}
     */
    update(source, direction) {
      console.log(source, direction, 'update')
      const dirRight = direction === 'r' ? 1 : -1 // 方向为右/左
      this.forUpward = direction == 'r'
      const tree = this.treeMap(this.root[direction])
      const nodes = tree.descendants()
      // 返回此节点及其后代的链接数组，
      const links = tree.links()
      var data = []
      if (nodes.length > 1) {
        for (var i = 0; i < nodes.length; i++) {
          if (!data[nodes[i].depth]) {
            var arr = []
            arr.push(nodes[i])
            data[nodes[i].depth] = arr
          } else {
            data[nodes[i].depth].push(nodes[i])
          }
        }
        // 检测最大长度
        this.testLength(data, dirRight)
      }

      // 根据中心点调整 x y
      nodes.forEach((d) => {
        d.y = dirRight * (d.y + this.rootNodeLength / 2) + this.centralPoint[1]
        d.x = d.x + this.centralPoint[0]
      })

      // 根据class名称获取左或者右的g节点，达到分块更新
      const className = `${this.d3Id}-${direction}gNode`
      const node = this.container
        .selectAll(`g.${className}`)
        .data(nodes, (d) => d.id)
      // 新增节点，tree会根据数据内的children扩展相关节点
      const that = this
      const nodeEnter = node
        .enter()
        .append('g')
        .attr('id', (d) => `g${d.id}`)
        .attr('class', className)
        .attr('style', (d) => {
          if (d.data.hasOwnProperty('crmType') && d.data.crmType !== 'follower') {
            return 'cursor: pointer'
          }
          return ''
        })
        .attr('transform', (d) => `translate(${source.y0},${source.x0})`)
        .attr('fill-opacity', 0)
        .attr('stroke-opacity', 0)
        .on('click', function(event, d) {
          console.log('详情点击', d)
          if (d.data.hasOwnProperty('crmType') && d.data.crmType !== 'follower') {
            that.detailId = d.data.id
            that.crmType = d.data.crmType
            that.showFullDetail = true
          }
        })

      nodeEnter.each((d) => {
        if (d.depth > 0) {
          this.drawText(`g${d.id}`, dirRight)

          this.drawRect(`g${d.id}`, dirRight)
          this.drawMarker(`g${d.id}`, dirRight)
        }

        if (d.depth > 0) {
          // const width = Math.min(d.data.name.length * 14, this.rectMinWidth)
          // const right = dirRight > 0
          // const xDistance = right ? width : -width
          this.drawCircle(`g${d.id}`, dirRight, source, direction)
          this.drawSign(`g${d.id}`, dirRight) // 画标记
        }

        // 画节点数量
        // console.log(d, "画节点数量");
        // if (d.data && d.data.type == -1) {
        //   this.drawLength(`g${d.id}`, dirRight)
        // }
      })

      // 更新节点：节点enter和exit时都会触发tree更新
      /* const nodeUpdate = */node
        .merge(nodeEnter)
        .transition()
        .duration(this.duration)
        .attr('transform', function(d) {
          // if (d.data && d.data.isNextNode) {
          //   d3.select(this)
          //     .selectAll(`.${this.d3Id}-node-circle .node-circle-vertical`)
          //     .transition()
          //     .duration(this.duration)
          //     .attr('stroke-width', function(d) {
          //       if (d.children) {
          //         return 0
          //       } else {
          //         return 1
          //       }
          //     })
          // }

          // var index = 0

          return 'translate(' + d.y + ',' + d.x + ')'
        })
        .attr('fill-opacity', 1)
        .attr('stroke-opacity', 1)

      // 移除节点:tree移除掉数据内不包含的节点(即，children = false)
      /* const nodeExit = */node
        .exit()
        .transition()
        .duration(this.duration)
        .remove()
        .attr('transform', (d) => `translate(${source.y},${source.x})`)
        .attr('fill-opacity', 0)
        .attr('stroke-opacity', 0)

      // Update the links 根据 className来实现分块更新
      const link = this.container
        .selectAll(`path.${className}`)
        .data(links, (d) => d.target.id)

      // Enter any new links at the parent's previous position.
      // insert是在g标签前面插入，防止连接线挡住G节点内容
      const linkEnter = link
        .enter()
        .insert('path', 'g')
        .attr('class', className)
        .attr('d', (d) => {
          const o = { x: source.x0, y: source.y0 }

          return this.diagonal({ source: o, target: o })
        })
        .attr('fill', 'none')
        .attr('stroke-width', 0.5)
        .attr('stroke', '#D8D8D8')

      // Transition links to their new position.
      link
        .merge(linkEnter)
        .transition()
        .duration(this.duration)
        .attr('d', this.diagonal)

      // Transition exiting nodes to the parent's new position.
      link
        .exit()
        .transition()
        .duration(this.duration)
        .remove()
        .attr('d', (d) => {
          const o = { x: source.x, y: source.y }

          return this.diagonal({ source: o, target: o })
        })

      // Stash the old positions for transition.
      this.root[direction].eachBefore((d) => {
        d.x0 = d.x
        d.y0 = d.y
      })
    },

    /**
     * @description: 画连接线
     * @param {*} source
     * @param {*} target
     * @return {*}
     */
    diagonal({ source, target }) {
      const s = source
      const d = target

      if (this.forUpward) {
        return (
          'M' +
          s.y +
          ',' +
          s.x +
          'L' +
          (s.y + (d.y - s.y) - 20) +
          ',' +
          s.x +
          'L' +
          (s.y + (d.y - s.y) - 20) +
          ',' +
          d.x +
          'L' +
          d.y +
          ',' +
          d.x
        )
      } else {
        return (
          'M' +
          s.y +
          ',' +
          s.x +
          'L' +
          (s.y + (d.y - s.y) + 20) +
          ',' +
          s.x +
          'L' +
          (s.y + (d.y - s.y) + 20) +
          ',' +
          d.x +
          'L' +
          d.y +
          ',' +
          d.x
        )
      }
    },

    /**
     * @description: 画箭头
     * @param {*} id 唯一id
     * @param {*} dirRight
     * @return {*}
     */
    drawMarker(id, dirRight) {
      const gMark = d3
        .select(`#${id}`)
        .append('g')
        .attr(
          'transform',
          dirRight > 0 ? 'translate(-20,0)' : 'translate(12,0)'
        )
      return gMark
        .insert('path', 'text')

        .attr('d', function(d) {
          if (d.data.nodeType == 0 && dirRight > 0) {
            return 'M0,0L0,3L9,0L0,-3Z'
          } else if (d.data.nodeType == 0 && dirRight < 0) {
            return 'M0,0L9,-3L9,3Z'
          }
        })
        .style('fill', (d) => d.data.nodeType === undefined ? '' : '#0052CC')
    },

    /**
     * @description: 画标记
     * @param {*} id
     * @param {*} dirRight
     * @return {*}
     */
    drawSign(id, dirRight) {
      return d3
        .select(`#${id}`)
        .insert('circle', 'text')
        .attr('cx', dirRight > 0 ? -5 : 8)
        .attr('y', -2.5)
        .attr('r', function(d) {
          if (d.data.nodeType == 0) {
            return 3
          }
        })
        .style('fill', (d) => this.getSignStorkeColor(d.data))
    },

    /**
     * @description: 获取大类型小球颜色
     * @param {*} name
     * @return {*}
     */
    getSignStorkeColor(data) {
      return {
        business: '#00B8D9',
        contacts: '#0052CC',
        contract: '#36B37E',
        invoice: '#FFAB00',
        follower: '#0052CC'
      }[data.dataType] || '#6B778C'
    },

    /**
     * @description: 画文本
     * @param {*} id 生成的唯一id
     * @param {*} dirRight // 右为1，左为-1
     * @return {*}
     */
    drawText(id, dirRight) {
      const that = this
      dirRight = dirRight > 0
      return d3
        .select(`#${id}`)
        .append('text')
        .attr('y', function(d) {
          return that.textPadding
        })
        .attr('x', (d) => (dirRight ? that.textPadding : d.data.name ? 0 : -that.textPadding))
        .attr('text-anchor', dirRight ? 'start' : 'end')
        .style('font-size', that.fontSize)
        .text(function(d) {
          const len = d.data.name?.length
          if (len > 20) {
            return d.data.name.substring(0, 20) + '...'
          } else {
            return d.data.name
          }
        })
        .attr('fill', function(d) { //  不同类型不同颜色
          if (d.data.nodeType == -1) {
            return 'rgb(33, 150, 243)'
          } else if (d.data.nodeType == 0) { // 大模块类型
            return '#172B4D'
          } else {
            return '#333'
          }
        })

        .attr('width', function(d) {
          // svg 的 text 有计算宽度的 API getComputedTextLength
          circlewidth1 = d3.select(this.getComputedTextLength())._groups[0][0]
          return d3.select(this.getComputedTextLength())._groups[0][0]
        })
    },

    /**
     * @description: 节点数量
     * @param {*} id
     * @return {*}
     */
    // drawLength(id) {
    //   return d3
    //     .select(`#${id} text`)
    //     .append('tspan')
    //     .attr('fill', (d) => '#999')
    //     .text(function(d) {
    //       if (d.data.type == -1) {
    //         return ' (' + d.data.val + ')'
    //       }
    //       return ' [' + d.data.ratio + ']'
    //     })
    //     .attr('fill', 'rgb(33, 150, 243)')
    //     .attr('width', function(d) {
    //       return d3.select(this.getComputedTextLength())._groups[0]
    //     })
    // },

    /**
     * @description: 画方框
     * @param {*} id 唯一id
     * @param {*} dirRight
     * @return {*}
     */
    drawRect(id, dirRight) {
      const that = this
      let realw = document.getElementById(id).getBBox().width + 25 // 获取g实际宽度后，设置rect宽度
      if (document.getElementById(id).getBBox().width > 400) {
        realw = 400
      }

      return d3
        .select(`#${id}`)
        .insert('rect', 'text')
        .attr('x', function(d) {
          if (dirRight < 0) {
            return -(realw + (d.data.isEndNode ? -20 : 0))
          } else {
            0
          }
        })
        .attr('y', function(d) {
          return -that.textSpace + that.textPadding - 4
        })
        .attr('width', function(d) {
          // if (d.data.isNextNode) {//判断是否有下级节点
          //     return realw
          // } else {
          //     return realw - 15
          // }
          if (d.data.name) {
            return realw + (d.data.isEndNode ? -15 : 5) // 如果是最后一个节点，减去20
          }
          return realw
        })
        .attr('height', function(d) {
          return that.textSpace + that.textPadding + 8
        })
        .attr('stroke-width', (d) => {
          d.data.nodeType == 0 || d.data.type == -1 ? 0 : 0.5
        }
        )
        .attr('rx', 2) // 圆角

        .style('stroke', (d) => that.getRectStorkeColor(d.data))
        .style('fill', function(d) {
          if (d.data.nodeType == 0) {
            return '#fff'
          } else {
            return '#fff'
          }
        })
    },

    /**
     * @description: 获取矩形边框颜色
     * @param {*} d
     * @return {*}
     */
    getRectStorkeColor(data) {
      if (data.nodeType == 0) {
        return ''
      } else {
        return {
          business: '#00B8D9',
          contacts: '#0052CC',
          contract: '#36B37E',
          invoice: '#FFAB00',
          receivablesPlan: '#6B778C',
          receivables: '#6554C0',
          visit: '#FFAB00',
          follower: '#0052CC'
        }[data.crmType] || '#6B778C'
      }
    },

    /**
     * @description: 画circle
     * @param {*} id
     * @param {*} dirRight
     * @param {*} source
     * @param {*} direction
     * @return {*}
     */
    drawCircle(id, dirRight, source, direction) {
      const that = this
      // console.log('drawCircle---', source.depth, source.children.lenght, source.children)
      const gMark = d3
        .select(`#${id}`)
        .append('g')
        .attr('class', `${this.d3Id}-node-circle`)
        .attr('style', 'cursor: pointer')
        .attr('stroke', 'rgb(153, 153, 153)')
        .attr('transform', function(d) {
          leng = Number(circlewidth1) + 15
          if (dirRight == 1) {
            return 'translate(' + leng + ',0)'
          } else {
            return 'translate(' + -leng + ',0)'
          }
        })
        // .attr('stroke-width', d => d.data.isNextNode ? 1 : 0);   判断是否有下级节点
        .attr('stroke-width', (d) => {
          // d.data.type == '-1'
          // console.log('big g ----', d)
          if ((d.children && d.children?.length > 0) || (d._children && d._children?.length > 0) ||
          (d.data?.isLoad === false)) {
            return 1
          }
          return 0
        })
        .on('click', function(event, d) {
          // console.log('node- 圆形', arguments)
          that.clickNodeElement = this
          that.clickNodeData = d

          if (d.depth !== 0) {
            that.clickNode(d, direction, source)
          }

          that.updateCircleVerticalLine(this)
          event.stopPropagation()
        })

      gMark
        .append('circle')
        .attr('fill', 'none')
        .attr('r', function(d) {
          // d.data.type == '-1'
          if ((d.children && d.children?.length > 0) || (d._children && d._children?.length > 0) ||
          (d.data?.isLoad === false)) {
            return 5
          }
          return 0
        }) // 根节点不设置圆圈
        .attr('fill', '#ffffff')
      const padding = this.circleR - 2

      gMark
        .append('path') // 横线
        .attr('d', `m -${padding} 0 l ${2 * padding} 0`)
        .attr('stroke-width', function(d) {
          // d.data.type == '-1'
          if ((d.children && d.children?.length > 0) || (d._children && d._children?.length > 0) ||
          (d.data?.isLoad === false)) {
            return 1
          }
          return 0
        })

      gMark
        .append('path') // 竖线，根据展开/收缩动态控制显示
        .attr('d', `m 0 -${padding} l 0 ${2 * padding}`)
        .attr('stroke-width', function(d) {
          // console.log('stroke-width-2---', d._children)
          if (d._children && d._children?.length > 0 ||
          (d.data?.isLoad === false)) {
            return 1
          }
          return 0
        })
        .attr('class', `${this.d3Id}-node-circle-vertical`)
      return gMark
    },

    /**
     * @description: 更新展开闭合线
     * @return {*}
     */
    updateCircleVerticalLine(node) {
      d3.select(node)
        .selectAll(`.${this.d3Id}-node-circle .${this.d3Id}-node-circle-vertical`)
        .transition()
        .duration(this.duration)
        .attr('stroke-width', function(d) {
          // 通过 _children 是否有值，来判断是展开还是闭合
          console.log('stroke-width-1---', d, d._children && d._children?.length > 0)
          if (d._children && d._children?.length > 0 ||
          (d.data?.isLoad === false)) {
            return 1
          }
          return 0
        })
    },

    expand(d, direction, source) {
      if (d.data?.isLoad === false) {
        this.getNextNodeData(d, direction, source) // 获取下级节点数据
      } else {
        if (d._children) {
          d.children = d._children

          d._children = null
          this.update(d, direction)
        }
      }
    },

    collapse(d, direction, obj) {
      if (d.children) {
        d._children = d.children
        d.children = null
        console.log(d._children, '_children')
      }
      if (obj == 1) {
        this.update(d, direction)
      }
    },

    /**
     * @description: 点击某个节点
     * @param {*} d
     * @param {*} direction
     * @param {*} source
     * @return {*}
     */
    clickNode(d, direction, source) {
      console.log('222-', d, direction, source)
      if (d.children || d.children) {
        this.collapse(d, direction, 1)
      } else {
        this.expand(d, direction, source)
      }
    },

    /**
     * @description: 获取展示模块筛选条件
     * @return {*}
     */
    getShowModuleFilterData() {
      const getModuleShow = (type) => {
        return !this.filterData || this.filterData && this.filterData.systemTypeArr.length && this.filterData.systemTypeArr.includes(type)
      }
      return {
        isBusiness: getModuleShow(1),
        isContacts: getModuleShow(2),
        isContract: getModuleShow(4),
        isInvoice: getModuleShow(5),
        isReceivablesPlan: getModuleShow(6),
        isReceivables: getModuleShow(7),
        isVisit: getModuleShow(8),
        isFollower: getModuleShow(3)
      }
    },

    /**
     * 获取下个节点 数据
     */
    getNextNodeData(d, direction, source) {
      const filterData = this.getShowModuleFilterData()

      const params = {
        customerId: d.data.id,
        ...filterData,
        relationsIds: this.filterData ? this.filterData.relationTypeArr : []
      }
      crmCustomerQueryCustomerAtlastListAPI(params)
        .then(res => {
          // 标记客户节点数据已加载
          d.data.isLoad = true
          const resData = res.data || {}
          const data = []
          this.setModuleData(filterData, data, resData)
          // let mynodes = []
          if (resData.relationsList.length) {
            this.setRightData(data, resData, params)
          }

          const appendData = {
            children: data
          }
          console.log(appendData, 111)
          this.DataReor(d, direction, source, appendData)
          this.updateCircleVerticalLine(this.clickNodeElement) // 展开节点
        })
        .catch(() => {})
    },

    // 工具条操作
    /**
     * @description: 全屏展示
     * @return {*}
     */
    onFullScreen() {
      const element = this.$refs.bar
      if (this.isScreenState) {
        if (document.exitFullscreen) {
          document.exitFullscreen()
        } else if (document.msExitFullscreen) {
          document.msExitFullscreen()
        } else if (document.mozCancelFullScreen) {
          document.mozCancelFullScreen()
        } else if (document.webkitExitFullscreen) {
          document.webkitExitFullscreen()
        }
        this.isScreenState = !this.isScreenState
      } else {
        if (element.requestFullscreen) {
          element.requestFullscreen()
        } else if (element.msRequestFullscreen) {
          element.msRequestFullscreen()
        } else if (element.mozRequestFullScreen) {
          element.mozRequestFullScreen()
        } else if (element.webkitRequestFullscreen) {
          element.webkitRequestFullscreen()
        }
        this.isScreenState = !this.isScreenState
      }
    },

    /**
     * @description: 放大SVG
     * @return {*}
     */
    onMagnifySVG() {
      console.log(this.svg)
      this.zoom.scaleBy(this.svg, 1.1) // 执行该方法后 会触发zoom事件
      d3.zoomTransform(this.svg.node())
    },

    /**
     * @description: 缩小SVG
     * @return {*}
     */
    onShrinkSVG() {
      this.zoom.scaleBy(this.svg, 0.9) // 执行该方法后 会触发zoom事件
      d3.zoomTransform(this.svg.node())
    },

    /**
     * @description: 下载图片
     * @param {*} node
     * @param {*} width
     * @param {*} height
     * @return {*}
     */
    onDownload(node, width, height) {
      // svg => canvas => png
      const serializer = new XMLSerializer()
      const source = '<?xml version="1.0" standalone="no"?>\r\n' + serializer.serializeToString(node)
      const image = new Image()
      image.src = 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(source)
      const canvas = document.createElement('canvas')
      canvas.width = width + 50
      canvas.height = height + 50
      const context = canvas.getContext('2d')
      context.fillStyle = '#fff'
      context.fillRect(0, 0, 10000, 10000)
      image.onload = () => {
        context.drawImage(image, 0, 0)
        const a = document.createElement('a')
        a.download = `${'download'}.png`
        a.href = canvas.toDataURL('image/png')
        a.click()
      }
    },

    /**
     * @description: 图片保存
     * @return {*}
     */
    svgDownloadAll() {
      // 得到svg的真实大小
      const svg = document.querySelector(`.${this.d3Id}-tree-svg`)
      var box = svg.getBBox()
      var x = box.x
      var y = box.y
      var width = box.width
      var height = box.height
      // 查找zoomObj
      // var zoomObj = svg.querySelector(`.${this.d3Id}-d3container`)
      // if (!zoomObj) {
      //   return false
      // }
      /* ------这里是处理svg缩放的--------*/
      // var transformMath = zoomObj.getAttribute('transform')
      // var scaleMath = zoomObj.getAttribute('transform')
      // if (transformMath || scaleMath) {
      //   var transformObj = transformMath.match(/translate\(([^,]*),([^,)]*)\)/)
      //   var scaleObj = scaleMath.match(/scale\((.*)\)/)
      //   if (transformObj || scaleObj) { // 匹配到缩放
      //     var translateX = transformObj[1]
      //     var translateY = transformObj[2]
      //     var scale = scaleObj[1]
      //     x = (x - translateX) / scale
      //     y = (y - translateY) / scale
      //     width = width / scale
      //     height = height / scale
      //   }
      // }
      /* ----------------------------------*/
      // 克隆svg
      var node = svg.cloneNode(true)
      // 重新设置svg的width,height,viewbox
      node.setAttribute('width', width)
      node.setAttribute('height', height)
      node.setAttribute('viewBox', [x, y, width, height])
      // var zoomObj = node.querySelector(`.${this.d3Id}-d3container`)
      /* -------------清楚缩放元素的缩放-------------*/
      node.setAttribute('transform', 'translate(0,0) scale(1)')
      this.onDownload(node, width, height)
    },

    /**
     * @description: 随机8位英文字母
     * @return {*}
     */
    randomString() {
      var chars = 'abcdefghijklmnopqrstuvwxyz'
      var result = ''
      for (var i = 0; i < 5; i++) {
        result += chars.charAt(Math.floor(Math.random() * chars.length))
      }
      return result
    }

  }
}
</script>
<style lang="scss" scoped>
.rc-cont {
  width: 100%;
  height: 100%;
}

.seeTree-page {
  width: 100%;
  height: 100%;
  background-color: #fff;
}

.d3-tree-box {
  position: relative;
  width: calc(100% - 10px);
  height: calc(100vh - 400px);
  margin-top: 10px;
}

::v-deep .d3-tree-svg {
  width: 100%;
  height: 100%;
}

.toolbar {
  position: absolute;
  top: 60px;
  right: 10px;
  z-index: 999;
  box-sizing: border-box;
  width: 44px;
  background-color: #fff;
  border: #bbb solid 1px;
  box-shadow: 0 0 8px #ccc;

  ul {
    display: flex;
    flex-direction: column;

    li {
      box-sizing: border-box;
      float: left;
      width: 42px;
      height: 44px;
      padding-top: 3px;
      margin-top: 0;
      font-size: 18px;
      line-height: 21px;
      color: #999;
      text-align: center;
      cursor: pointer;
      background-color: #fff;
      border-top: #efefef solid 1px;
      opacity: 1;

      i {
        width: 16px;
        height: 16px;
        overflow: hidden;
        vertical-align: -3px;
        fill: currentcolor;
      }

      span {
        position: absolute;
        display: inline-block;
        width: 42px;
        height: 14px;
        margin-top: 24px;
        margin-left: -28px;
        font-size: 12px;
        line-height: 12px;
        color: #262626;
      }
    }

    li:hover {
      i,
      span {
        color: #fff !important;
      }

      background-color: #2e4e8f;
      border-top: #2e4e8f solid 1px;
    }
  }
}
</style>
